package com.tannuo.dolphinnotedemo.sdk.device;

import android.os.SystemClock;

import com.tannuo.dolphinnotedemo.sdk.util.Logger;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * Created by hucn on 2016/8/2.
 * Description: 缓存点数据的简单实现
 */
public class SimpleCache implements IPointsCache {

    private final String TAG = SimpleCache.class.getSimpleName();

    private static final int DEFAULT_CACHE_SIZE = 1000 * 100;

    private static final float HYSTERESIS_FACTOR = 0.5f;

    private long mTotalSize = 0;

    private int mMaxCacheSize = DEFAULT_CACHE_SIZE;

    // 需要保存的路径
    private File mDirectory;

    private File mDefaultSavedFile;

    private ArrayList<Byte> pointsData = new ArrayList<>();

    // TODO: 2016/8/2 点的长度还未决定！！！！！！！！！！！！！！！！！
    private final int elementLength = 12;

    // 每次存储的步长
    private final int savedStepLength = 1000;

    public SimpleCache() {

    }

    @Override
    public void initialize() {
        if (mDirectory != null) {
            if (!mDirectory.exists()) {
                if (!mDirectory.mkdirs()) {
                    Logger.e("Unable to create cache dir %s", mDirectory.getAbsolutePath());
                }
            }

            // 考虑到可能退出重进，所以不删除
            /*File[] files = mDirectory.listFiles();
            if (files != null) {
                for (File file : files) {
                    file.delete();
                }
            }*/

            if (mDirectory.exists()) {
                if (mDefaultSavedFile != null) {
                    boolean created = false;
                    if (!mDefaultSavedFile.exists()) {
                        try {
                            created = mDefaultSavedFile.createNewFile();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if (!created) {
                            Logger.e("Unable to create cache file %s", mDefaultSavedFile.getAbsolutePath());
                        }
                    } else {
                        Logger.d(TAG, "Cached file exist");
                    }
                }
            } else {
                Logger.d(TAG, "Cached directory exist");
            }

        } else {
            Logger.e(TAG, "Cache directory is null!");
        }
    }

    @Override
    public void putData(byte[] data) {
        pruneIfNeeded(data.length);
        for (byte b : data) {
            pointsData.add(b);
            mTotalSize++;
        }
    }

    @Override
    public byte[] getCachedData() {
        byte[] bytes = new byte[pointsData.size()];
        for (int i = 0; i < pointsData.size(); i++) {
            bytes[i] = pointsData.get(i);
        }
        return bytes;
    }

    @Override
    public void clearCache() {
        pointsData.clear();
        mTotalSize = 0;
    }

    @Override
    public void clear() {
        pointsData.clear();
        mTotalSize = 0;
        Logger.d(TAG, "Cache cleared.");
    }

    @Override
    public void remove() {
        pointsData.clear();
        mTotalSize = 0;
        if (mDirectory.exists()) {
            File[] files = mDirectory.listFiles();
            if (files != null) {
                for (File file : files) {
                    file.delete();
                }
            }
            Logger.d(TAG, "Cache and files removed.");
        } else {
            Logger.e(TAG, "Cache file not exits.");
        }
    }

    @Override
    public void snapshot() {
        if (mDefaultSavedFile != null) {
            copyFile(mDefaultSavedFile, new File(getSnapshotName()));
        }
    }

    @Override
    public void cacheFromFile(String file) {
        byte[] data = readPointsFile(file);
        if (data != null) {
            for (byte b : data) {
                pointsData.add(b);
                mTotalSize++;
            }
        }
    }

    @Override
    public void setCacheSize(int size) {
        mMaxCacheSize = size;
    }

    @Override
    public void setCacheFile(File file) {
        mDirectory = file;
        mDefaultSavedFile = new File(file, getFileName());
    }

    private String getFileName() {
        // TODO: 2016/8/2 确定一个命名规则
        return "default";
    }

    private String getSnapshotName() {
        // TODO: 2016/8/2 确定一个命名规则
        Date nowTime = new Date();
        System.out.println(nowTime);
        SimpleDateFormat time = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.CHINA);
        return "snapshot" + time;
    }

    /**
     * 如果计算的最大缓存大小超过了设置的大小，那么把前面的点保存的文件中
     */
    private void pruneIfNeeded(int extraSpace) {
        if ((mTotalSize + extraSpace) < mMaxCacheSize) {
            return;
        }
        Logger.d(TAG, "Pruning old cache pointd.");
        long startTime = SystemClock.elapsedRealtime();
        long before = mTotalSize;
        while (mTotalSize > mMaxCacheSize * HYSTERESIS_FACTOR) {
            List<Byte> subList = pointsData.subList(0, elementLength * savedStepLength);
            int size = subList.size();
            byte[] bytes = new byte[size];
            for (int i = 0; i < subList.size(); i++) {
                bytes[i] = subList.get(i);
            }
            BufferedOutputStream bos = null;
            try {
                bos = new BufferedOutputStream(new FileOutputStream(mDefaultSavedFile, true));
                bos.write(bytes);
                for (int i = 0; i < subList.size(); i++) {
                    pointsData.remove(0);
                    mTotalSize--;
                }
            } catch (FileNotFoundException e) {
                Logger.e(TAG, "Could not open the cached file");
                e.printStackTrace();
            } catch (IOException e) {
                Logger.e(TAG, "Save cache data to file failed");
                e.printStackTrace();
            } finally {
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        //Logger.d("pruned %d bytes, %d ms", (mTotalSize - before), SystemClock.elapsedRealtime() - startTime);

    }

    /**
     * 读取文件中的点数据
     */
    public byte[] readPointsFile(String filename) {
        File f = new File(filename);
        if (!f.exists()) {
            Logger.e(TAG, "Point data file is not exits");
            return null;
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
        BufferedInputStream bis = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(f));
            int buf_size = 1024;
            byte[] buffer = new byte[buf_size];
            int len;
            while (-1 != (len = bis.read(buffer, 0, buf_size))) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            Logger.e(TAG, "Read point data from file error！");
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }


    /**
     * 保存快照到一个新的文件中
     */
    public void copyFile(File cached, File snapshot) {
        if (mDefaultSavedFile == null) {
            Logger.e(TAG, "Cached file is null ,can't snapshot");
            return;
        }

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(cached));
            bos = new BufferedOutputStream(new FileOutputStream(snapshot));
            byte[] buffer = new byte[1024];
            int length;
            while ((length = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, length);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
